using Furion;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

using NLog;

using Serilog;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer;

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Unicode;

namespace Tool
{
    public static partial class AppEx
    {
        public static IServiceCollection InitMvc(this IServiceCollection services)
        {



            return services;
        }

        #region NLog

        ///// <summary>
        ///// Web 主机注入
        ///// </summary>
        ///// <param name="hostBuilder">Web主机构建器</param>
        ///// <returns>IWebHostBuilder</returns>
        //public static IWebHostBuilder UseNLog(this IWebHostBuilder hostBuilder)
        //{
        //    //hostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, assemblyName);
        //    hostBuilder.UseNLog();
        //    return hostBuilder;
        //}

        #endregion NLog

        #region Serilog  日志

        ///// <summary>
        ///// 添加默认日志拓展
        ///// </summary>
        ///// <param name="builder"></param>
        ///// <param name="configAction"></param>
        ///// <returns></returns>
        //public static IHostBuilder UseSerilogDefault(this IHostBuilder builder, Action<LoggerConfiguration> configAction = default)
        //{
        //    builder.UseSerilog((hostingContext, services, loggerConfiguration) =>
        //    {
        //        // 加载配置文件
        //        var config = loggerConfiguration
        //            .ReadFrom.Configuration(hostingContext.Configuration)
        //            .Enrich.FromLogContext();

        //        if (configAction != null) configAction.Invoke(config);
        //        else
        //        {
        //            config.WriteTo.Console(
        //                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}")
        //                .WriteTo.File(Path.Combine("logs", "log.txt"), rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true);
        //        }
        //    });

        //    return builder;
        //}

        /// <summary>
        /// 日志模板
        /// //"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}" + new string('-', 50);
        /// </summary>
        public static readonly string LogTemplate = "{NewLine}Date：{Timestamp:yyyy-MM-dd HH:mm:ss.fff} LogLevel：{Level}{NewLine}Message：{Message}{Properties:j}{NewLine}{NewLine}{Exception}" + new string('-', 50);

        /// <summary>
        /// 日志对象集合
        /// </summary>
        private static ConcurrentDictionary<string, Serilog.ILogger> Loggers = new ConcurrentDictionary<string, Serilog.ILogger>();

        /// <summary>
        /// 获取 Serilog Logger
        /// </summary>
        /// <param name="LogFloderName">日志文件夹名称</param>
        /// <param name="IsLogEventLevelFloder">是否每个事件一个文件夹</param>
        /// <param name="rollOnFileSizeLimit">如果为true，则在达到文件大小限制时将创建一个新文件。文件名将以_NNN格式附加一个数字，第一个文件名没有数字。</param>
        /// <param name="minLogEventLevel">最低日志等级，默认Debug</param>
        /// <param name="rollingInterval">滚动间隔：日志将滚动到新文件的间隔</param>
        /// <param name="retainedFileCountLimit">保留的最大日志文件数，包括当前</param>
        /// <param name="flushToDiskInterval">TimeSpan 不为null，将定期在指定的时间执行完整磁盘刷新间隔。</param>
        /// <param name="buffered">指示是否可以缓存到输出文件。默认值 false</param>
        /// <param name="shared">允许多个进程共享日志文件。默认值为false</param>
        /// <param name="fileSizeLimitBytes">文件大小限制字节：允许日志文件访问的近似最大大小（以字节为单位）成长。对于不受限制的增长，传递null。默认值为1 GB。避免写作部分事件，限制内的最后一个事件将全部写入偶数如果超过限制</param>
        /// <param name="outputTemplate">日志输出模板</param>
        /// <param name="filename">文件名</param>
        /// <returns></returns>
        public static Serilog.ILogger GetLogger(
                                        // string path,
                                        // LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,

                                        // LoggingLevelSwitch levelSwitch = null,

                                        string logFloderName = "default",//日志文件夹名称
                                        bool isLogEventLevelFloder = true,
                                        //ITextFormatter formatter,
                                        bool rollOnFileSizeLimit = true,//如果为true，则在达到文件大小限制时将创建一个新文件。文件名将以_NNN格式附加一个数字，第一个文件名没有数字。
                                                                        // Encoding encoding = null,
                                                                        // // FileLifecycleHooks hooks = null,
                                                                        //
                                                                        //
                                        LogEventLevel minLogEventLevel = LogEventLevel.Debug,//最低日志等级，默认Debug
                                        RollingInterval rollingInterval = RollingInterval.Hour,//滚动间隔：日志将滚动到新文件的间隔
                                        int? retainedFileCountLimit = null,//保留的最大日志文件数，包括当前
                                        TimeSpan? flushToDiskInterval = null,//TimeSpan 不为null，将定期在指定的时间执行完整磁盘刷新间隔。
                                        bool buffered = false,//指示是否可以缓存到输出文件。默认值 false
                                        bool shared = false,//允许多个进程共享日志文件。默认值为false
                                        long? fileSizeLimitBytes = 1073741824L,//文件大小限制字节：允许日志文件访问的近似最大大小（以字节为单位）成长。对于不受限制的增长，传递null。默认值为1 GB。避免写作部分事件，限制内的最后一个事件将全部写入偶数如果超过限制
                                        string outputTemplate = "",
                                        string filename = "")
        {
            if (string.IsNullOrWhiteSpace(logFloderName))
            {
                logFloderName = "default";
            }
            else
            {
                logFloderName = logFloderName.Trim("\\");
            }
            if (!string.IsNullOrWhiteSpace(filename))
            {
                filename = filename.Trim("\\");
            }
            Serilog.ILogger logger = null;
            if (Loggers.ContainsKey(logFloderName))
            {
                var state = Loggers.TryGetValue(logFloderName, out logger);
            }
            else
            {
                outputTemplate = LogTemplate;//日志输出模板
                if (!string.IsNullOrWhiteSpace(App.Configuration["Serilog:Customer:OutputTemplate"]))
                {
                    outputTemplate = App.Configuration["Serilog:Customer:OutputTemplate"];
                }
                try
                {
                    // if (string.IsNullOrWhiteSpace(outputTemplate)) outputTemplate = LogTemplate;
                    minLogEventLevel = App.Configuration["Serilog:Customer:MinLogEventLevel"]?.To<Serilog.Events.LogEventLevel>() ?? Serilog.Events.LogEventLevel.Debug;
                    rollingInterval = App.Configuration["Serilog:Customer:RollingInterval"]?.To<Serilog.RollingInterval>() ?? Serilog.RollingInterval.Hour;
                    rollOnFileSizeLimit = App.Configuration["Serilog:Customer:RollOnFileSizeLimit"]?.To<bool>() ?? true;
                    buffered = App.Configuration["Serilog:Customer:Buffered"]?.To<bool>() ?? false;
                    shared = App.Configuration["Serilog:Customer:Shared"]?.To<bool>() ?? false;

                    var tempFileSizeLimitBytes = App.Configuration["Serilog:Customer:FileSizeLimitBytes"];
                    if (!string.IsNullOrWhiteSpace(tempFileSizeLimitBytes))
                    {
                        fileSizeLimitBytes = tempFileSizeLimitBytes.To<long>();
                    }

                    var tempRetainedFileCountLimit = App.Configuration["Serilog:Customer:RetainedFileCountLimit"];
                    if (!string.IsNullOrWhiteSpace(tempRetainedFileCountLimit))
                    {
                        retainedFileCountLimit = tempRetainedFileCountLimit.To<int>();
                    }

                    var tempFlushToDiskInterval = App.Configuration["Serilog:Customer:FlushToDiskInterval"];
                    if (!string.IsNullOrWhiteSpace(tempFlushToDiskInterval))
                    {
                        flushToDiskInterval = tempFlushToDiskInterval.To<TimeSpan>();
                    }

                    isLogEventLevelFloder = App.Configuration["Serilog:Customer:IsLogEventLevelFloder"].To<bool>();//是否按日志类别分类记录
                }
                catch (Exception e)
                {
                }
                if (isLogEventLevelFloder)
                {
                    logger = new Serilog.LoggerConfiguration()
                        .SetMinimumLevel(minLogEventLevel)
                        .SetWriteTo(logFloderName,//日志文件夹名称
                                                  //ITextFormatter formatter,
                                                  rollOnFileSizeLimit,
                                                  // Encoding encoding = null,
                                                  // FileLifecycleHooks hooks = null,
                                                  minLogEventLevel,
                                                  rollingInterval,
                                                  retainedFileCountLimit,
                                                  flushToDiskInterval,
                                                  buffered,
                                                  shared,
                                                  fileSizeLimitBytes,
                                                  outputTemplate,
                                                  filename)
                        .CreateLogger();
                }
                else
                {
                    logger = new Serilog.LoggerConfiguration()
                                                 .SetMinimumLevel(minLogEventLevel)
                                                 .WriteTo.File($"logs\\{logFloderName}\\log.txt", rollingInterval: rollingInterval, rollOnFileSizeLimit: rollOnFileSizeLimit, buffered: buffered, shared: shared, fileSizeLimitBytes: fileSizeLimitBytes,
                                                     retainedFileCountLimit: retainedFileCountLimit, flushToDiskInterval: flushToDiskInterval, outputTemplate: outputTemplate)
                                                 .CreateLogger();
                }
                Loggers.TryAdd(logFloderName, logger);
            }
            if (logger == null) logger = Serilog.Log.Logger;
            return logger;
        }

        private static Serilog.LoggerConfiguration SetWriteTo(this Serilog.LoggerConfiguration loggerConfiguration,// string path,
                                                                                                                   // LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,

                                        // LoggingLevelSwitch levelSwitch = null,
                                        string LogFloderName = "",//日志文件夹名称

                                        bool rollOnFileSizeLimit = true,
                                        // Encoding encoding = null,
                                        // FileLifecycleHooks hooks = null,
                                        LogEventLevel minLogEventLevel = LogEventLevel.Debug,
                                        RollingInterval rollingInterval = RollingInterval.Hour,
                                        int? retainedFileCountLimit = 100,
                                        TimeSpan? flushToDiskInterval = null,
                                        bool buffered = false,
                                        bool shared = false,
                                        long? fileSizeLimitBytes = 1073741824L,
                                        string outputTemplate = "",
                                        string filename = "")
        {
            var isConsole = App.Configuration["Serilog:Customer:IsConsole"]?.To<bool>() ?? false;
            if (isConsole)
            {
                loggerConfiguration.WriteTo.Console(outputTemplate: outputTemplate);
            }
            var isFile = App.Configuration["Serilog:Customer:IsFile"]?.To<bool>() ?? true;
            if (isFile)
            {
                foreach (Serilog.Events.LogEventLevel logEventLevel in Enum.GetValues(typeof(Serilog.Events.LogEventLevel)))
                {
                    loggerConfiguration.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == logEventLevel)
                        .WriteTo.File(LogFilePath(LogFloderName, logEventLevel, filename),
                            rollingInterval: rollingInterval, outputTemplate: outputTemplate,
                            rollOnFileSizeLimit: rollOnFileSizeLimit, fileSizeLimitBytes: fileSizeLimitBytes,
                            retainedFileCountLimit: retainedFileCountLimit, flushToDiskInterval: flushToDiskInterval,
                            shared: shared));
                }
            }

            var isMsSql = App.Configuration["Serilog:Customer:IsMsSql"]?.To<bool>() ?? false;
            if (isMsSql)
            {
                //var columnOpts = new ColumnOptions();
                //columnOpts.Store.Remove(StandardColumn.Properties);
                //columnOpts.Store.Add(StandardColumn.LogEvent);
                //columnOpts.LogEvent.DataLength = 2048;
                //columnOpts.PrimaryKey = options.TimeStamp;
                //columnOpts.TimeStamp.NonClusteredIndex = true;
                var columnOptions = new ColumnOptions();

                var connectionString = App.Configuration["Serilog:Customer:MSSQLSERVER:ConnectionString"];
                var tableName = App.Configuration["Serilog:Customer:MSSQLSERVER:TableName"] ?? "SeriLogs";
                var batchPostingLimit = App.Configuration["Serilog:Customer:MSSQLSERVER:batchPostingLimit"]?.ToInt32() ?? 50;
                var minimumLevel = App.Configuration["Serilog:Customer:MSSQLSERVER:restrictedToMinimumLevel"]?.To<LogEventLevel>() ?? LogEventLevel.Verbose;

                var sinkOpts = new MSSqlServerSinkOptions();
                sinkOpts.TableName = tableName;
                sinkOpts.BatchPostingLimit = batchPostingLimit;
                sinkOpts.BatchPeriod = TimeSpan.FromSeconds(3);//执行时间间隔
                sinkOpts.AutoCreateSqlTable = true;

                loggerConfiguration.WriteTo.MSSqlServer(
                    connectionString: connectionString,
                    sinkOptions: sinkOpts,
                    // tableName: tableName,
                    columnOptions: columnOptions,
                    // batchPostingLimit: batchPostingLimit,//批量插入数据库条数
                    // period: TimeSpan.FromSeconds(5),//执行时间间隔
                    restrictedToMinimumLevel: minimumLevel
                //autoCreateSqlTable: true
                );
            }

            return loggerConfiguration;
        }

        private static Serilog.LoggerConfiguration SetMinimumLevel(this Serilog.LoggerConfiguration loggerConfiguration, Serilog.Events.LogEventLevel minLevel)
        {
            switch (minLevel)
            {
                case LogEventLevel.Verbose:
                    return loggerConfiguration.MinimumLevel.Verbose();

                case LogEventLevel.Debug:
                    return loggerConfiguration.MinimumLevel.Debug();

                case LogEventLevel.Information:
                    return loggerConfiguration.MinimumLevel.Information();

                case LogEventLevel.Warning:
                    return loggerConfiguration.MinimumLevel.Warning();

                case LogEventLevel.Error:
                    return loggerConfiguration.MinimumLevel.Error();

                case LogEventLevel.Fatal:
                    return loggerConfiguration.MinimumLevel.Fatal();

                default:
                    break;
            }
            return loggerConfiguration.MinimumLevel.Debug();
        }

        private static string LogFilePath(string LogFloderName, Serilog.Events.LogEventLevel loglevel, string filename)
        {
            if (!string.IsNullOrWhiteSpace(filename))
            {
                return $@"logs\{LogFloderName}\{filename}_.txt";
            }
            else
            {
                return $@"logs\{LogFloderName}\{loglevel:G}_.txt";
            }
        }

        #endregion Serilog  日志

        #region NLog

        private static ConcurrentDictionary<string, NLog.Logger> NLoggers = new ConcurrentDictionary<string, NLog.Logger>();

        /// <summary>
        /// 全局属性参数【初始化设定】
        /// </summary>
        public static Dictionary<string, string> GlobalProperties = new Dictionary<string, string>();

        /// <summary>
        /// 获取Logger ，按 logFloder 昨晚Key缓存
        /// </summary>
        /// <param name="logFloder"></param>
        /// <param name="filename"></param>
        /// <param name="withPropertys">字典</param>
        /// <returns></returns>
        public static NLog.Logger GetNLogger(
            string logFloder = "default",
            string filename = "",
            Dictionary<string, string> withPropertys = null)
        {
            if (string.IsNullOrWhiteSpace(logFloder))
            {
                logFloder = "default";
            }
            NLog.Logger logger = null;
            if (NLoggers.ContainsKey(logFloder))
            {
                var state = NLoggers.TryGetValue(logFloder, out logger);
            }
            else
            {
                //logger = LogManager.GetCurrentClassLogger();
                logger = LogManager.GetLogger(logFloder);
                if (string.IsNullOrWhiteSpace(logFloder)) logFloder = "default";
                PropertiesAdd(logger, "logfloder", logFloder.Trim());
                if (!string.IsNullOrWhiteSpace(filename)) PropertiesAdd(logger, "filename", $"{filename.Trim()}-");

                if (withPropertys != null)
                {
                    foreach (var withProperty in withPropertys)
                    {
                        if (string.IsNullOrWhiteSpace(withProperty.Key)) continue;
                        PropertiesAdd(logger, withProperty.Key.Trim(), withProperty.Value?.Trim() ?? "");
                    }
                }
                NLoggers.TryAdd(logFloder, logger);
            }
            return logger;
        }

        private static NLog.Logger PropertiesAdd(NLog.Logger logger, string key, object value)
        {
            if (logger.Properties.ContainsKey(key)) return logger;
            logger.Properties.Add(key, value);
            return logger;
        }

        #endregion NLog

        #region 访问计数，访问防重复执行

        /// <summary>
        /// 访问计数，防止锁死
        /// </summary>
        private static ConcurrentDictionary<string, int> MethodUses = new ConcurrentDictionary<string, int>();

        /// <summary>
        /// 获取访问次数
        /// 每次调用方法，对应key的值都会加 1，创建的新值默认为1
        /// </summary>
        /// <param name="key">关键词</param>
        /// <param name="maxcount">最大计数，超过此计数会清0</param>
        /// <returns></returns>
        public static int GetMethodUseCount(string key, int maxcount = -1)
        {
            if (MethodUses.ContainsKey(key))
            {
                MethodUses[key] = MethodUses[key] + 1;
                if (maxcount > 0 && MethodUses[key] > maxcount)
                {
                    MethodUses[key] = 0;
                    return 0;
                }
                else
                {
                    return MethodUses[key];
                }
            }
            else
            {
                MethodUses.TryAdd(key, 1);
                return 1;
            }
        }

        /// <summary>
        /// 清理访问次数，清理后的访问次数为0
        /// </summary>
        /// <param name="key"></param>
        public static void ClearMethodUseCount(string key)
        {
            if (MethodUses.ContainsKey(key))
            {
                MethodUses[key] = 0;
            }
        }

        #endregion 访问计数，访问防重复执行

        ///// <summary>
        ///// Web 主机注入
        ///// </summary>
        ///// <param name="hostBuilder">Web主机构建器</param>
        ///// <param name="assemblyName">外部程序集名称</param>
        ///// <returns>IWebHostBuilder</returns>
        //public static IWebHostBuilder Inject(this IWebHostBuilder hostBuilder, string assemblyName = nameof(Furion))
        //{
        //    hostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, assemblyName);
        //    return hostBuilder;
        //}

        ///// <summary>
        ///// 获取  System.Text.Json 默认设置
        ///// </summary>
        ///// <returns></returns>
        //public static System.Text.Json.JsonSerializerOptions GetDefaultJsonSerializerOptions()
        //{
        //    var options = new JsonSerializerOptions();
        //    options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All);//设置虚拟化中文的时候不编码
        //    return options;
        //}

        /// <summary>
        /// 获取  System.Text.Json 默认设置
        /// </summary>
        /// <returns></returns>
        public static System.Text.Json.JsonSerializerOptions GetDefaultJsonSerializerOptions
        {
            get
            {
                var options = new JsonSerializerOptions();
                options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All);//设置虚拟化中文的时候不编码
                return options;
            }
        }

        /// <summary>
        /// 默认 Newtonsoft.Json 配置
        /// </summary>
        /// <returns></returns>
        public static JsonSerializerSettings GetDefaultJsonSerializerSettings
        {
            get
            {
                var setting = new JsonSerializerSettings
                {
                    //WriteIndented = true,   // 缩进
                    //Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,  // 中文乱码
                    //PropertyNameCaseInsensitive = true  // 忽略大小写
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,//指定如何处理循环引用，None--不序列化，Error-抛出异常，Serialize--仍要序列化
                    MissingMemberHandling = MissingMemberHandling.Ignore,//控制在反序列化期间如何处理缺少的成员（例如JSON包含对象上不是成员的属性）。 Ignore 忽略，error 报错
                    ContractResolver = new CamelCasePropertyNamesContractResolver(),
                    Formatting = Formatting.Indented,//格式化 缩进
                    MaxDepth = 10, //设置序列化的最大层数
                    NullValueHandling = NullValueHandling.Ignore,//空值处理
                                                                 //日期类型默认格式化处理
                    DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat,
                    DateFormatString = "yyyy-MM-dd HH:mm:ss",
                };

                return setting;
            }
        }

        
    }
}